package org.ecloud.auth.server.provider;

import java.net.URISyntaxException;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.ecloud.auth.server.service.OAuthService;
import org.ecloud.base.i18n.LocaleMessageSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/** 
 * auth2.0 访问令牌控制器
 *
 * <pre> 
 * project: ecloud-auth-server
 * author: eddy
 * email: xqxyxchy@126.com
 * date: 2018年4月2日-下午8:20:59
 * rights: eddy
 * </pre>
 */
@RestController
@RequestMapping("/token/")
public class TokenProvider {

	@Autowired
	private LocaleMessageSource localeMessageSource;
	@Resource
	private OAuthService oAuthService;

	/**
	 * 获取访问token，大写部分是参数
	 * 
	 * <pre>
	 * https://api.weibo.com/oauth2/access_token?
	 * 		client_id=YOUR_CLIENT_ID&
	 * 		client_secret=YOUR_CLIENT_SECRET&
	 * 		grant_type=authorization_code&
	 * 		redirect_uri=YOUR_REGISTERED_REDIRECT_URI&
	 * 		code=CODE
	 * 
	 * https://api.weibo.com/oauth2/access_token?
	 * 		client_id=YOUR_CLIENT_ID&
	 * 		client_secret=YOUR_CLIENT_SECRET&
	 * 		grant_type=refresh_token&
	 * 		redirect_uri=YOUR_REGISTERED_REDIRECT_URI&
	 * 		refresh_token=YOUR_REFRESH_TOKEN
	 * 
	 * {
	 *	    "access_token": "SlAV32hkKG",
	 *	    "refresh_token": "SlAV32jfydWG",
	 *	    "remind_in": 3600,
	 *	    "expires_in": 3600
	 *	}
	 * </pre>
	 */
	@RequestMapping("access_token")
	@ResponseBody
	public Object accessToken(HttpServletRequest request) throws URISyntaxException, OAuthSystemException {
		try {
			// 构建OAuth请求
			OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);

			// 检查提交的客户端id是否正确
			if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
				OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
						.setError(OAuthError.TokenResponse.INVALID_CLIENT)
						.setErrorDescription(localeMessageSource.getMessage("invalid.client"))
						.buildJSONMessage();
				return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
			}

			// 检查客户端安全KEY是否正确
			if (!oAuthService.checkClientIdSecret(oauthRequest.getClientId(), oauthRequest.getClientSecret())) {
				OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
						.setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
						.setErrorDescription(localeMessageSource.getMessage("invalid.client"))
						.buildJSONMessage();
				return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
			}

			// 检查验证类型，此处只检查AUTHORIZATION_CODE类型，其他的还有PASSWORD或REFRESH_TOKEN
			String grantType = oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE);
			if (GrantType.AUTHORIZATION_CODE.toString().equals(grantType)) {
				return requestByCode(oauthRequest);
			}else if (GrantType.REFRESH_TOKEN.toString().equals(grantType)) {
				return requestByRefreshToken(oauthRequest);
			}else{
				OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
						.setError(OAuthError.TokenResponse.UNSUPPORTED_GRANT_TYPE)
						.setErrorDescription(localeMessageSource.getMessage("invalid.grant", new Object[]{grantType}))
						.buildJSONMessage();
				return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
			}
		} catch (OAuthProblemException e) {
			// 构建错误响应
			OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
					.buildJSONMessage();
			return new ResponseEntity<String>(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
		}
	}

	private HttpEntity<String> requestByRefreshToken(OAuthTokenRequest oauthRequest) throws OAuthSystemException {
		String refreshToken = oauthRequest.getParam(OAuth.OAUTH_REFRESH_TOKEN);
		if (!oAuthService.checkRefreshToken(refreshToken)) {
			OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
					.setError(OAuthError.TokenResponse.INVALID_REQUEST)
					.setErrorDescription(localeMessageSource.getMessage("invalid.refresh.token"))
					.buildJSONMessage();
			return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
		}
		
		// 生成新Token
		OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
		final String accessToken = oauthIssuerImpl.accessToken();
		oAuthService.addAccessToken(accessToken, oAuthService.getClientIdByRefreshToken(refreshToken));
		final String newRefreshToken = oauthIssuerImpl.accessToken();
		oAuthService.addRefreshToken(newRefreshToken, oAuthService.getClientIdByRefreshToken(refreshToken));

		// 生成OAuth响应
		OAuthResponse response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK)
				.setAccessToken(accessToken)
				.setRefreshToken(newRefreshToken)
				.setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
				.buildJSONMessage();

		oAuthService.removeRefreshToken(refreshToken);
		
		// 根据OAuthResponse生成ResponseEntity
		return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
	}

	private HttpEntity<String> requestByCode(OAuthTokenRequest oauthRequest) throws OAuthSystemException {
		String authCode = oauthRequest.getParam(GrantType.AUTHORIZATION_CODE.toString());
		if (!oAuthService.checkAuthCode(authCode)) {
			OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
					.setError(OAuthError.TokenResponse.INVALID_GRANT)
					.setErrorDescription(localeMessageSource.getMessage("invalid.auth.code"))
					.buildJSONMessage();
			return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
		}
		
		// 生成Access Token
		OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
		final String accessToken = oauthIssuerImpl.accessToken();
		oAuthService.addAccessToken(accessToken, oAuthService.getClientIdByAuthCode(authCode));
		final String refreshToken = oauthIssuerImpl.accessToken();
		oAuthService.addRefreshToken(refreshToken, oAuthService.getClientIdByAuthCode(authCode));

		// 生成OAuth响应
		OAuthResponse response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK)
				.setAccessToken(accessToken)
				.setRefreshToken(refreshToken)
				.setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
				.buildJSONMessage();

		oAuthService.removeAuthCode(authCode);
		
		// 根据OAuthResponse生成ResponseEntity
		return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
	}

}
